package com.fly.demo;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Test;

public class TheStream
{
    /**
     * Stream中间操作 1.forEach() 循环每一个元素
     */
    @Test
    public static void StreamForEcach()
    {
        /**
         * 数组
         */
        System.out.println("--------------------数组的forEach()--------------------");
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        Arrays.stream(nums)
            .forEach(n -> {
                // 用forEach()遍历数组无法获取下标
                System.out.println("n = " + n);
            });
        
        /**
         * List
         */
        System.out.println("--------------------List的forEach()--------------------");
        List<String> list = new ArrayList<>();
        list.add("灰太狼");
        list.add("喜羊羊");
        list.add("小灰灰");
        list.add("懒羊羊");
        list.stream().forEach(l -> {
            System.out.println("index = " + list.indexOf(l) + "    value = " + l);
        });
        
        /**
         * map
         */
        System.out.println("-------------------Map的forEach()--------------------");
        Map<String, String> map = new HashMap<>();
        map.put("狼1", "灰太狼");
        map.put("狼2", "小灰灰");
        map.put("羊1", "喜羊羊");
        map.put("羊2", "懒羊羊");
        
        // map遍历方式1
        System.out.println("map遍历方式1：");
        map.keySet().forEach(key -> {
            System.out.println("key : " + key + "    value : " + map.get(key));
        });
        
        // map遍历方式2
        System.out.println("map遍历方式2：");
        map.entrySet().forEach(entry -> {
            System.out.println("key : " + entry.getKey() + "    value : " + entry.getValue());
        });
        
        // map遍历方式3
        System.out.println("map遍历方式3:");
        map.forEach((key, value) -> {
            System.out.println("key : " + key + "    value : " + value);
        });
        
        /**
         * set
         */
        System.out.println("--------------------set的forEach()--------------------");
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        set.forEach(s -> {
            System.out.println(s);
        });
    }
    
    /**
     * Stream中间操作 2.filter() 过滤筛选出符合某些条件的项，filter(条件 && 条件)
     */
    @Test
    public static void StreamFilter()
    {
        /**
         * 数组
         */
        System.out.println("--------------------数组的filter()--------------------");
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        // 筛选出除了10的所有偶数
        int[] newNums = Arrays.stream(nums).filter(n -> (n % 2 == 0 && n != 10)).toArray();
        Arrays.stream(newNums).forEach(System.out::println);
        
        /**
         * List
         */
        System.out.println("--------------------List的filter()--------------------");
        List<String> list = new ArrayList<>();
        list.add("灰太狼");
        list.add("喜羊羊");
        list.add("小灰灰");
        list.add("懒羊羊");
        // 筛选出以"羊"结尾的项
        List<String> newList = list.stream().filter(l -> l.endsWith("羊")).collect(Collectors.toList());
        newList.forEach(System.out::println);
        
        /**
         * map
         */
        System.out.println("--------------------Map的filter()--------------------");
        Map<String, String> map = new HashMap<>();
        map.put("狼1", "灰太狼");
        map.put("狼2", "小灰灰");
        map.put("羊1", "喜羊羊");
        map.put("羊2", "懒羊羊");
        // 通过以狼结尾的项筛选出灰太狼
        Map<String, String> newMap = map.entrySet().stream().filter(m -> {
            if (m.getKey().startsWith("狼") && m.getValue().equals("灰太狼"))
            {
                return true;
            }
            else
            {
                return false;
            }
        }).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
        
        newMap.forEach((k, v) -> {
            System.out.println("key = " + k + "     value = " + v);
        });
        
        /**
         * set
         */
        System.out.println("--------------------Set的filter()--------------------");
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        // 筛选出偶数
        Set<Integer> newSet = set.stream().filter(s -> s % 2 == 0).collect(Collectors.toSet());
        newSet.forEach(System.out::println);
        
    }
    
    /**
     * Stream中间操作 3.limit() 它是用于限制流中元素的个数，即取前n个元素，返回新的流
     */
    @Test
    public static void StreamLimit()
    {
        /**
         * 数组
         */
        System.out.println("--------------------数组的limit()--------------------");
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        // 取出前10个数
        int[] newNums = Arrays.stream(nums).limit(5).toArray();
        Arrays.stream(newNums).forEach(System.out::println);
        
        /**
         * List
         */
        System.out.println("--------------------List的limit()--------------------");
        List<String> list = new ArrayList<>();
        list.add("灰太狼");
        list.add("喜羊羊");
        list.add("小灰灰");
        list.add("懒羊羊");
        // 取出前两项
        List<String> newList = list.stream().limit(2).collect(Collectors.toList());
        newList.forEach(System.out::println);
        
        /**
         * map
         */
        System.out.println("--------------------Map的limit()--------------------");
        Map<String, String> map = new HashMap<>();
        map.put("狼1", "灰太狼");
        map.put("狼2", "小灰灰");
        map.put("羊1", "喜羊羊");
        map.put("羊2", "懒羊羊");
        // 取出前两项
        Map<String, String> newMap = map.entrySet().stream().limit(2).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
        
        newMap.forEach((k, v) -> {
            System.out.println("key = " + k + "     value = " + v);
        });
        
        /**
         * set
         */
        System.out.println("--------------------Set的limit()--------------------");
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        // 取出前两项
        Set<Integer> newSet = set.stream().limit(2).collect(Collectors.toSet());
        newSet.forEach(System.out::println);
        
    }
    
    /**
     * Stream中间操作 4.skip() 该方法与limit()方法互补 skip()方法用于跳过前面n个元素，然后再返回新的流
     */
    @Test
    public static void StreamSkip()
    {
        
        /**
         * 方法skip(n)的参数n的四种情况：
         *
         * （1）当n<0时，抛IllegalArgumentException异常；
         *
         * （2）当n=0时，相当没有跳过任何元素，原封不动、完璧归赵；
         *
         * （3）当0<n<length时，跳过n个元素后，返回含有剩下的元素的流；
         *
         * （4）当n>=length时，跳过所有元素，返回空流。
         */
        
        /**
         * 数组
         */
        System.out.println("--------------------数组的skip()--------------------");
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        // 跳过前5项返回其余的项
        int[] newNums = Arrays.stream(nums).skip(5).toArray();
        Arrays.stream(newNums).forEach(System.out::println);
        
        /**
         * List
         */
        System.out.println("--------------------List的skip()--------------------");
        List<String> list = new ArrayList<>();
        list.add("灰太狼");
        list.add("喜羊羊");
        list.add("小灰灰");
        list.add("懒羊羊");
        //// 跳过前2项返回其余的项
        List<String> newList = list.stream().skip(2).collect(Collectors.toList());
        newList.forEach(System.out::println);
        
        /**
         * map
         */
        System.out.println("--------------------Map的skip()--------------------");
        Map<String, String> map = new HashMap<>();
        map.put("狼1", "灰太狼");
        map.put("狼2", "小灰灰");
        map.put("羊1", "喜羊羊");
        map.put("羊2", "懒羊羊");
        // 跳过前2项返回其余的项
        Map<String, String> newMap = map.entrySet().stream().skip(2).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
        
        newMap.forEach((k, v) -> {
            System.out.println("key = " + k + "     value = " + v);
        });
        
        /**
         * set
         */
        System.out.println("--------------------Set的skip()--------------------");
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        // 跳过前2项返回其余的项
        Set<Integer> newSet = set.stream().skip(2).collect(Collectors.toSet());
        newSet.forEach(System.out::println);
    }
    
    /**
     * Stream中间操作 5.distinct() 去除重复项
     */
    @Test
    public static void StreamDistinct()
    {
        /**
         * 数组
         */
        System.out.println("--------------------数组的distinct()--------------------");
        int[] nums = {1, 2, 3, 1, 2, 34, 4, 5, 5, 8, 4};
        // 去除重复项
        int[] newNums = Arrays.stream(nums).distinct().toArray();
        Arrays.stream(newNums).forEach(System.out::println);
        
        /**
         * List
         */
        System.out.println("--------------------List的distinct()--------------------");
        List<String> list = new ArrayList<>();
        list.add("灰太狼");
        list.add("灰太狼");
        list.add("喜羊羊");
        list.add("喜羊羊");
        list.add("懒羊羊");
        // 去除重复项
        List<String> newList = list.stream().distinct().collect(Collectors.toList());
        newList.forEach(System.out::println);
        
        /**
         * map和set因为不可重复性所以没必要用到distinct()
         */
        
    }
    
    /**
     * Stream中间操作 6.map() 可以将对象转换为其他对象 Stream.map()是Stream最常用的一个转换方法，它把一个Stream转换为另一个Stream
     * 所谓map操作，就是把一种操作运算，映射到一个序列的每一个元素上。 例如，对x计算它的平方，可以使用函数f(x) = x * x。我们把这个函数映射到一个序列1，2，3，4，5上，就得到了另一个序列1，4，9，16，25：
     */
    @Test
    public static void StreamMap()
    {
        // 将数组先转为Stream,再平方后再转为数组
        int[] nums = {1, 2, 3, 4, 5};
        int[] result1 = Arrays.stream(nums).map(n -> n * n).toArray();
        
        // 将Strings列表转换为大写
        List<String> list1 = Arrays.asList("a", "b", "c", "d", "e");
        List<String> newList1 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());
        newList1.forEach(System.out::println);
        
        // 对象列表 - >字符串列表
        List<User> users = Arrays.asList(new User(1, "灰太狼", new Date(), 11, "男"), new User(2, "喜羊羊", new Date(), 10, "男"), new User(3, "小灰灰", new Date(), 15, "男"));
        List<String> newUsers = users.stream().map(u -> u.getName()).collect(Collectors.toList());
        newUsers.forEach(System.out::println);
        
        // 对象列表 - >其他对象列表(User -> Account)
        List<Account> accounts = users.stream().map(u -> {
            Account account = new Account();
            account.setId(u.getId());
            account.setName(u.getName());
            return account;
        }).collect(Collectors.toList());
        accounts.forEach(System.out::println);
        
    }
    
    /**
     * Stream中间操作 7.sorted() 排序
     */
    @Test
    public static void StreamSorted()
    {
        // 将数组先转为Stream,再平方后再转为数组
        int[] nums = {11, 2, 32, 14, 5};
        int[] newNums = Arrays.stream(nums).sorted().toArray();
        Arrays.stream(newNums).forEach(System.out::println);
        
        // 按对象的年龄排序
        List<User> users = Arrays.asList(new User(1, "灰太狼", new Date(), 11, "男"), new User(2, "喜羊羊", new Date(), 10, "男"), new User(3, "小灰灰", new Date(), 15, "男"));
        List<User> newUsers = users.stream().sorted((p1, p2) -> {
            return p1.getAge().compareTo(p2.getAge());
        }).collect(Collectors.toList());
        newUsers.forEach(System.out::println);
        
    }
    
    /**
     * Stream终止操作 1.allMatch() 检查是否匹配所有元素
     */
    @Test
    public static void StreamAllMatch()
    {
        // 判断数组的元素是否都是在0-20之间
        int[] nums = {11, 2, 32, 14, 5};
        boolean isBetweenZeroTwenty = Arrays.stream(nums).allMatch(n -> (n >= 0) && (n <= 20));
        System.out.println("数组的元素是否都是在0-20之间:" + isBetweenZeroTwenty);
        
        // 判断user列表的元素的年纪是否都是大于等于18岁
        List<User> users = Arrays.asList(new User(1, "灰太狼", new Date(), 11, "男"), new User(2, "喜羊羊", new Date(), 10, "男"), new User(3, "小灰灰", new Date(), 15, "男"));
        boolean isAdult = users.stream().allMatch(u -> u.getAge() >= 18);
        System.out.println("user列表的元素的年纪是否都是大于等于18岁:" + isAdult);
    }
    
    /**
     * Stream终止操作 2.max()和 min() 查找最大值
     */
    @Test
    public static void StreamMaxMin()
    {
        // 返回数组最大值
        int[] nums = {11, 2, 32, 14, 5};
        int numMax = Arrays.stream(nums).max().getAsInt();
        int numMin = Arrays.stream(nums).min().getAsInt();
        System.out.println("数组最大值：" + numMax);
        System.out.println("数组最小值：" + numMin);
        
        // 返回年纪最大值
        List<User> users = Arrays.asList(new User(1, "灰太狼", new Date(), 11, "男"), new User(2, "喜羊羊", new Date(), 10, "男"), new User(3, "小灰灰", new Date(), 15, "男"));
        Optional<User> userMax = users.stream().max((p, q) -> {
            return p.getAge().compareTo(q.getAge());
        });
        
        Optional<User> userMin = users.stream().min((p, q) -> {
            return p.getAge().compareTo(q.getAge());
        });
        System.out.println("年纪最大的是 : " + userMax.get());
        System.out.println("年纪最小的是 : " + userMin.get());
        
    }
    
    /**
     * Stream终止操作 3.sum()和 average() 查找最大值
     */
    @Test
    public static void StreamSumAve()
    {
        // 求和与平均值
        int[] nums = {11, 2, 32, 14, 5};
        int numSum = Arrays.stream(nums).sum();// 求和
        double numAve = Arrays.stream(nums).average().getAsDouble();// 求平均值
        System.out.println("求和 ：" + numSum);
        System.out.println("平均值：" + numAve);
        
        // 对象的求和与平均值
        List<User> users = Arrays.asList(new User(1, "灰太狼", new Date(), 11, "男"), new User(2, "喜羊羊", new Date(), 10, "男"), new User(3, "小灰灰", new Date(), 15, "男"));
        Integer userAgeSum = users.stream().map(u -> u.getAge()).reduce(Integer::sum).get();
        Double userAgeAve = users.stream().collect(Collectors.averagingInt(p -> p.getAge()));
        
        System.out.println("用户年纪求和 : " + userAgeSum);
        System.out.println("用户年纪平均值 : " + userAgeAve);
        
    }
    
    /**
     * Stream高级应用 1.排序并分组
     *
     */
    @Test
    public static void StreamGroupingOrderBy()
    {
        try
        {
            SimpleDateFormat simFormat = new SimpleDateFormat("yyyy/MM/dd");
            // 对象的求和与平均值
            List<User> users = Arrays.asList(new User(1, "灰太狼", simFormat.parse("2018/01/23"), 11, "男"),
                new User(2, "红太狼", simFormat.parse("2018/01/23"), 10, "女"),
                new User(2, "美羊羊", simFormat.parse("2001/12/03"), 10, "女"),
                new User(3, "小灰灰", simFormat.parse("2022/02/13"), 15, "男"),
                new User(3, "懒羊羊", simFormat.parse("2020/02/13"), 15, "男"));
            
            // 按性别分组
            Map<String, List<User>> collectSex = users.stream().sorted(Comparator.comparing(User::getBirthday)).collect(Collectors.groupingBy(User::getSex));
            // 输出
            collectSex.forEach((k, v) -> {
                System.out.println("key : " + k + "    value : " + v);
            });
            // 按出生日期的day分组
            Map<Date, List<User>> collectBirthday = users.stream().sorted(Comparator.comparing(User::getBirthday)).collect(Collectors.groupingBy(u -> u.getBirthday()));
            
            // 输出
            collectBirthday.forEach((k, v) -> {
                System.out.println("key : " + simFormat.format(k) + "    value : " + v);
            });
            
        }
        catch (ParseException e)
        {
            e.printStackTrace();
        }
    }
    
    /**
     * Stream高级应用 2.集合之间转换
     *
     */
    @Test
    public static void StreamConvert()
    {
        
        try
        {
            /**
             * List ----> Map
             */
            SimpleDateFormat simFormat = new SimpleDateFormat("yyyy/MM/dd");
            List<User> users = Arrays.asList(new User(1, "灰太狼", simFormat.parse("2018/01/23"), 11, "男"),
                new User(2, "红太狼", simFormat.parse("2018/01/23"), 10, "女"),
                new User(2, "美羊羊", simFormat.parse("2001/12/03"), 10, "女"),
                new User(3, "小灰灰", simFormat.parse("2022/02/13"), 15, "男"),
                new User(3, "懒羊羊", simFormat.parse("2020/02/13"), 15, "男"));
            
            // 以name为key,age为value,将list<User>转为Map
            Map<String, Integer> map = users.stream().collect(Collectors.toMap(User::getName, User::getAge));
            map.forEach((key, value) -> {
                System.out.println("key:" + key + "    value:" + value);
            });
            
            /**
             * List -----> Set
             */
            Set<User> collect1 = users.stream().collect(Collectors.toSet());
            collect1.forEach(System.out::println);
            
            /**
             * Map -----> List
             */
            List<String> stringList = map.entrySet().stream().map(m -> m.getKey()).collect(Collectors.toList());
            stringList.forEach(System.out::println);
            
            /**
             * Map ----> Set
             */
            Set<String> stringSet = map.entrySet().stream().map(m -> m.getKey()).collect(Collectors.toSet());
            stringSet.forEach(System.out::println);
            
            /**
             * Set ----> List
             */
            List<String> list = stringSet.stream().collect(Collectors.toList());
            list.forEach(System.out::println);
            
        }
        catch (ParseException e)
        {
            e.printStackTrace();
        }
        
    }
    
}

class User
{
    private Integer id;
    
    private String name;
    
    private Date birthday;
    
    private Integer age;
    
    private String sex;
    
    @Override
    public String toString()
    {
        return "User{" + "id=" + id + ", name='" + name + '\'' + ", birthday=" + birthday + ", age=" + age + ", sex='" + sex + '\'' + '}';
    }
    
    public User(Integer id, String name, Date birthday, Integer age, String sex)
    {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
        this.age = age;
        this.sex = sex;
    }
    
    public String getSex()
    {
        return sex;
    }
    
    public void setSex(String sex)
    {
        this.sex = sex;
    }
    
    public Integer getAge()
    {
        return age;
    }
    
    public void setAge(Integer age)
    {
        this.age = age;
    }
    
    public void setId(Integer id)
    {
        this.id = id;
    }
    
    public void setName(String name)
    {
        this.name = name;
    }
    
    public void setBirthday(Date birthday)
    {
        this.birthday = birthday;
    }
    
    public Integer getId()
    {
        return id;
    }
    
    public String getName()
    {
        return name;
    }
    
    public Date getBirthday()
    {
        return birthday;
    }
}

class Account
{
    private Integer id;
    
    private String name;
    
    public Account()
    {
    }
    
    @Override
    public String toString()
    {
        return "Account{" + "id=" + id + ", name='" + name + '\'' + '}';
    }
    
    public Account(Integer id, String name)
    {
        this.id = id;
        this.name = name;
    }
    
    public Integer getId()
    {
        return id;
    }
    
    public void setId(Integer id)
    {
        this.id = id;
    }
    
    public String getName()
    {
        return name;
    }
    
    public void setName(String name)
    {
        this.name = name;
    }
}
